using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;


namespace TLingenf.SharePoint.CustomFieldTypes
{
    /// <summary>
    /// Control used to render PasswordField columns on list forms.
    /// </summary>
    public class PasswordFieldControl : BaseFieldControl
    {
        #region Globals: Constants and Variables

        // rendering template control constants
        private const string PROP_EDT_TMPL = "SPFieldPasswordEditCtrl";
        private const string PROP_DIS_TMPL = "SPFieldPasswordDisplayCtrl";
        private const string CTRL_TXT_ID = "PasswordTextBox";
        private const string CTRL_BTN_ID = "GenerateButton";
        private const string CTRL_LBL_ID = "DisplayLabel";

        /* Establish the range of ASCII values that characters for a generated password will use.
         * This value range has been chosen because it encompasses all numerical, digit, and special characters 
         * that a password may contain. */
        private const int VAL_ASC_MIN = 33;
        private const int VAL_ASC_MAX = 125;

        /* In order to prevent an infinate loop if the password generator is unable to validate against
         * field's validated string value method, limit the number of times that the control will attempt
         * to generate a password.  This could happen if there is something wrong with the regular expression
         * used to validate the password. */
        private const int VAL_MAX_TRY = 20;

        // rendering controls
        protected TextBox PasswordTextBox;
        protected Button GenerateButton;
        protected Label DisplayLabel;

        #endregion

        #region TemplatedBasedControl: RenderingTemplate Names

        /// <summary>
        /// Rendering template used for Edit and New forms.
        /// </summary>
        protected override string DefaultTemplateName
        {
            get { return PROP_EDT_TMPL; }
        }

        /// <summary>
        /// Rendering template used for display forms.
        /// </summary>
        public override string DisplayTemplateName
        {
            get { return PROP_DIS_TMPL; }
        }

        #endregion

        #region Field Control: Control and Value Method Overrides

        /// <summary>
        /// Gets or sets the rendering control's value.
        /// </summary>
        public override object Value
        {
            get
            {
                EnsureChildControls();

                // depending on the current ControlMode, retrieve the value of appropriate control object
                // that is currently being used to display/edit the PasswordField's value on the form.
                switch (this.ControlMode)
                {
                    case SPControlMode.Display:
                        if (this.DisplayLabel != null)
                            return this.DisplayLabel.Text;
                        break;

                    default:
                        if (this.PasswordTextBox != null)
                            return this.PasswordTextBox.Text;
                        break;
                }

                // could not get a reference to the display/edit control in use
                return null;
            }
            set
            {
                EnsureChildControls();

                // set the value of the presentation control currently in use depending on the current ControlMode
                switch (this.ControlMode)
                {
                    case SPControlMode.Display:
                        if (this.DisplayLabel != null)
                            this.DisplayLabel.Text = value.ToString();
                        break;

                    default:
                        if (this.PasswordTextBox != null)
                            this.PasswordTextBox.Text = value.ToString();
                        break;
                }               
            }
        }

        protected override void CreateChildControls()
        {
            // load the control template before attempting to assign values to controls
            base.CreateChildControls();

            PasswordField pwdField = this.Field as PasswordField;

            if (pwdField != null)
            {
                switch (this.ControlMode)
                {
                    // obtain reference to presentation control and initialize values used on display forms
                    case SPControlMode.Display:
                        this.DisplayLabel = this.TemplateContainer.FindControl(CTRL_LBL_ID) as Label;
                        if (this.DisplayLabel != null)
                        {
                            this.DisplayLabel.Text = this.ItemFieldValue as string;
                        }
                        break;

                    // obtain reference to presentation control and initialize values used on edit and new forms
                    default:
                        this.PasswordTextBox = this.TemplateContainer.FindControl(CTRL_TXT_ID) as TextBox;
                        this.GenerateButton = this.TemplateContainer.FindControl(CTRL_BTN_ID) as Button;

                        // determine if the user has permission to edit and enable/disable controls appropriately,
                        // don't worry about displaying the value or the mask to hide the value if the user 
                        // does not have permission.  It will be loaded from the field's value.
                        if (pwdField.UserHasEditPermission(SPContext.Current.Web.CurrentUser))
                        {
                            // enable the controls *ONLY* if the user has the appropriate permission
                            this.PasswordTextBox.Enabled = true;
                            this.GenerateButton.Enabled = true;
                            this.GenerateButton.Click += new EventHandler(GenerateNewPassword);
                        }
                        else
                        {
                            // the user does not have permission, disable the controls
                            this.PasswordTextBox.Enabled = false;
                            this.GenerateButton.Enabled = false;
                        }
                        break;
                }
            }
        }

        #endregion

        #region PasswordFieldControl: Events and Helper Methods

        /// <summary>
        /// Generates a password that passes the current validation for the password control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void GenerateNewPassword(object sender, EventArgs e)
        {
            // random character generator seeded with the current millisecond value
            Random rnd = new Random(DateTime.Now.Millisecond);

            StringBuilder builder = null;
            PasswordField pwdField = this.Field as PasswordField;

            if ((pwdField != null) && (this.PasswordTextBox != null))
            {
                // initialize password value flag and generation attempt counter
                bool pwdIsValid = false;
                int counter = 0;

                // attempt to generate the password.
                do
                {
                    // generate a random string of characters using the current minimum number of characters
                    // allowed for the field's value
                    builder = new StringBuilder();

                    for (int i = 0; i < pwdField.MinChars; i++)
                    {
                        // Get the next character using the established ASCII values range
                        builder.Append((char)rnd.Next(VAL_ASC_MIN, VAL_ASC_MAX));
                    }

                    // determine if the generated password will conform to the field's validation method
                    try
                    {
                        pwdField.GetValidatedString(builder.ToString());

                        // validation was successful, flag password as valid
                        pwdIsValid = true;
                    }
                    catch (SPFieldValidationException)
                    {
                        // could not validate, increment the generate attempt counter and flag to try again
                        pwdIsValid = false;
                        counter++;
                    }
                }
                /* terminate if password has been flagged as valid or the max generation
                 * attempts have been reached */
                while ((!pwdIsValid) && (counter < VAL_MAX_TRY));

                // assign the generated password to the control's value
                this.Value = builder.ToString();
            }
        }

        #endregion
    }
}